package com.k.kpp.exception;

import com.k.kc.common.resp.ErrorResult;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全局异常类
 */
@ControllerAdvice
public class GlobalException {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalException.class);

    private String getPath(HttpServletRequest request) {
        return request.getRequestURI();
    }

    private ErrorResult buildErrorResult(Exception e, HttpServletRequest request, HttpStatus httpStatus) {
        LOGGER.error("Error:", e);
        String code = httpStatus.value() + "";
        String message = httpStatus.getReasonPhrase();
        String path = getPath(request);
        List<String> errors = new ArrayList<>();
        if (e instanceof BindException) {
            BindingResult bindingResult = ((BindException) e).getBindingResult();
            List<ObjectError> objectErrors = bindingResult.getAllErrors();
            errors = objectErrors.stream().map(a -> {
                String field = ((FieldError) a).getField();
                String errorMessage = a.getDefaultMessage();
                return field + ":" + errorMessage;
            }).collect(Collectors.toList());
        } else if (e instanceof ConstraintViolationException) {
            Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) e).getConstraintViolations();
            if (violations != null) {
                errors = violations.stream().map(violation -> {
                    PathImpl path1 = (PathImpl) violation.getPropertyPath();
                    String paramName = path1.getLeafNode().getName();
                    String errorMessage = violation.getMessage();
                    return paramName + ":" + errorMessage;
                }).collect(Collectors.toList());
            }
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            errors = Collections.singletonList(((HttpRequestMethodNotSupportedException) e).getMethod() + ":" + e.getMessage());
        } else {
            errors = Collections.singletonList(e.getMessage());
        }
        return ErrorResult.format(code, message, path, errors);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResult> handleEx(Exception e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.INTERNAL_SERVER_ERROR);
        return new ResponseEntity<>(errorData, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(value = BindException.class)
    public ResponseEntity<ErrorResult> handleBindException(BindException e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.BAD_REQUEST);
        return new ResponseEntity<>(errorData, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResult> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.BAD_REQUEST);
        return new ResponseEntity<>(errorData, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(DataAccessException.class)
    public ResponseEntity<ErrorResult> handleJdbc1(DataAccessException e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.INTERNAL_SERVER_ERROR);
        return new ResponseEntity<>(errorData, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(CannotGetJdbcConnectionException.class)
    public ResponseEntity<ErrorResult> handleJdbc2(CannotGetJdbcConnectionException e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.INTERNAL_SERVER_ERROR);
        return new ResponseEntity<>(errorData, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ErrorResult> handleRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        ErrorResult errorData = buildErrorResult(e, request, HttpStatus.BAD_REQUEST);
        return new ResponseEntity<>(errorData, HttpStatus.BAD_REQUEST);
    }
}
